package application.mvc.sys.config.fpr;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.Base64;
import java.util.ResourceBundle;


import com.jfinal.kit.StrKit;
import com.jfinal.log.Log;
import com.zkteco.biometric.FingerprintSensorErrorCode;
import com.zkteco.biometric.FingerprintSensorEx;

import application.common.model.User;
import application.common.util.FiltUtil;
import application.common.util.LayerUtil;
import application.common.util.RouteUtil;
import application.common.util.ShiroUtil;
import javafx.animation.Animation;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.RotateTransition;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.util.Duration;

public class FprController implements Initializable{

	private final Log log = Log.getLog(getClass());

	
	@FXML
	private Label headerLabel;
	
	@FXML
	private Label copyrightLabel;
	
	@FXML
	private Label usernameLabel;
	
	@FXML
	private Label roleNameLabel;
	
	@FXML
	private ImageView portraitImageView;

	@FXML
	private Pane timerOutLinePane;

	@FXML
	private Pane timerInLinePane;
	
	@FXML
	private Button firstButton;
	
	@FXML
	private Button secondButton;
	
	@FXML
	private Label messageLabel;
	
	@FXML
	private Label timerLabel;
	private Timeline animation;
	private RotateTransition rotateTransitionOut;
	private RotateTransition rotateTransitionIn;
	
	@FXML
	private Label firstEnrollLabel;
	
	@FXML
	private Label secondEnrollLabel;

	private boolean isDestroy=false;
	
	private ObjectProperty<User> user = new SimpleObjectProperty<User>();
	
	public FprController(User user) {
		if(user!=null) {
			if(!StrKit.isBlank(user.getZp())) {
				Image image = new Image(new ByteArrayInputStream(Base64.getDecoder().decode(user.getZp())));
				user.setPortrait(image);
			}else {
				user.setPortrait(new Image(getClass().getResourceAsStream("/application/resources/aabb.jpeg")));
			}
		}
		this.user.set(user);
	}
	
	public void initialize(URL location, ResourceBundle resources) {
		
		headerLabel.setText(ShiroUtil.getSoftName());
		copyrightLabel.setText(ShiroUtil.getCopyright());
		
		if(user!=null && user.get()!=null) {
			portraitImageView.setImage(user.get().getPortrait());
			usernameLabel.setText(user.get().getName());
			roleNameLabel.setText(user.get().getRoleName());
			
			if(StrKit.isBlank(user.get().getFpr1())) {
				firstEnrollLabel.setText("待采集");
				firstEnrollLabel.setId("warning");
			}else {
				firstEnrollLabel.setText("已采集");
				firstEnrollLabel.setId("success");
			}
			if(StrKit.isBlank(user.get().getFpr2())) {
				secondEnrollLabel.setText("待采集");
				secondEnrollLabel.setId("warning");
			}else {
				secondEnrollLabel.setText("已采集");
				secondEnrollLabel.setId("success");
			}
			
		}else {
			firstEnrollLabel.setText("待采集");
			secondEnrollLabel.setText("待采集");
			firstEnrollLabel.setId("warning");
			secondEnrollLabel.setId("warning");
		}
		
		if(!isDestroy) {
			rotateTransitionOut = new RotateTransition(Duration.seconds(10), timerOutLinePane);
			rotateTransitionOut.setFromAngle(0);
			rotateTransitionOut.setToAngle(360);
			rotateTransitionOut.setByAngle(360); // 设置旋转的角度
			rotateTransitionOut.setCycleCount(Animation.INDEFINITE); // 设置旋转次数，我们需要旋转无数次
			rotateTransitionOut.setInterpolator(Interpolator.LINEAR);
			rotateTransitionOut.play();

			rotateTransitionIn = new RotateTransition(Duration.seconds(10), timerInLinePane);
			rotateTransitionIn.setFromAngle(360);
			rotateTransitionIn.setToAngle(0);
			rotateTransitionIn.setByAngle(360); // 设置旋转的角度
			rotateTransitionIn.setCycleCount(Animation.INDEFINITE); // 设置旋转次数，我们需要旋转无数次
			rotateTransitionIn.setInterpolator(Interpolator.LINEAR);
			rotateTransitionIn.play();
			startCountDown();
		}
	}
	
	public void startCountDown() {
		animation = new Timeline(new KeyFrame(Duration.millis(1000), new EventHandler<ActionEvent>() {
			public void handle(ActionEvent event) {
				if(RouteUtil.getTime()>0) {
					RouteUtil.setTime(RouteUtil.getTime() - 1);
					if (RouteUtil.getTime() == 0) {
						timerLabel.setText("0");
						LayerUtil.open("操作超时！", 999);
					} else if (RouteUtil.getTime() > 0) {
						timerLabel.setText(RouteUtil.getTime() + "");
					}
				}
				
			}
		}));
		animation.setCycleCount(Timeline.INDEFINITE);
		animation.play();
	}
	
	private void stop() {
		if (animation != null) {
			animation.stop();
		}
		if (rotateTransitionOut != null) {
			rotateTransitionOut.stop();
		}
		if (rotateTransitionIn != null) {
			rotateTransitionIn.stop();
		}
	}

	public void destroy() {
		stop();
		freeSensor();
	}

	public void exit(ActionEvent event) {
		if(!isDestroy) {
			isDestroy=true;
			destroy();
			RouteUtil.goIndex();
		}
	}

	private int curPos=0;
	public void firstFpr(ActionEvent event) {
		try {
			initFpr();
			firstEnrollLabel.setText("采集中");
			firstEnrollLabel.setId("normal");
			secondButton.setVisible(false);
			firstButton.setVisible(false);
			curPos=1;
		}catch (Exception e) {
			log.error("指纹采集异常:",e);
			secondButton.setVisible(true);
			firstButton.setVisible(true);
			message(e.getMessage(),false);
			curPos=0;
			Platform.runLater(new Runnable() {
				public void run() {
					firstEnrollLabel.setText("失败");
				}
			});
		}
	}
	
	public void secondFpr(ActionEvent event) {
		try {
			initFpr();
			secondEnrollLabel.setId("normal");
			secondEnrollLabel.setText("采集中");
			secondButton.setVisible(false);
			firstButton.setVisible(false);
			curPos=2;
		}catch (Exception e) {
			log.error("指纹采集异常:",e);
			secondButton.setVisible(true);
			firstButton.setVisible(true);
			message(e.getMessage(),false);
			curPos=0;
			Platform.runLater(new Runnable() {
				public void run() {
					secondEnrollLabel.setText("失败");
				}
			});
		}
	}
	
	/************************************************
	 * 指纹识别
	 ********************************************************/
	int fpWidth = 0;
	int fpHeight = 0;
	static final int enroll_cnt = 3;
	private byte[] imgbuf = null;
	private byte[] template = new byte[2048];
	private int[] templateLen = new int[1];
	private boolean mbStop = true;
	private long mhDevice = 0;
	private long mhDB = 0;
	private WorkThread workThread = null;

	private void initFpr() throws Exception{
		
		fpWidth = 0;
		fpHeight = 0;
		imgbuf = null;
		template = new byte[2048];
		templateLen = new int[1];
		mbStop = true;
		mhDevice = 0;
		mhDB = 0;
		
		regtemparray = new byte[3][2048];
		iFid = 1;
		nFakeFunOn = 1;
		enroll_idx = 0;
		lastRegTemp = new byte[2048];
		cbRegTemp = 0;
		
		
		if (0 != mhDevice) {
			throw new Exception("指纹仪已占用!");
		}
		int ret = FingerprintSensorErrorCode.ZKFP_ERR_OK;
		// Initialize
		if (FingerprintSensorErrorCode.ZKFP_ERR_OK != FingerprintSensorEx.Init()) {
			throw new Exception("指纹仪初始化失败!");
		}
		ret = FingerprintSensorEx.GetDeviceCount();
		if (ret < 0) {
			freeSensor();
			throw new Exception("指纹仪连接异常!");
		}
		if (0 == (mhDevice = FingerprintSensorEx.OpenDevice(0))) {
			freeSensor();
			throw new Exception(String.format("指纹仪开启失败，错误代码【%s】!", ret));
		}
		if (0 == (mhDB = FingerprintSensorEx.DBInit())) {
			freeSensor();
			throw new Exception(String.format("指纹仪算法库初始化失败，错误代码【%s】!", ret));
		}

		byte[] paramValue = new byte[4];
		int[] size = new int[1];
		size[0] = 4;
		FingerprintSensorEx.GetParameters(mhDevice, 1, paramValue, size);
		fpWidth = byteArrayToInt(paramValue);
		size[0] = 4;
		FingerprintSensorEx.GetParameters(mhDevice, 2, paramValue, size);
		fpHeight = byteArrayToInt(paramValue);
		imgbuf = new byte[fpWidth * fpHeight];
		mbStop = false;
		
		log.info("指纹仪开启成功！");
		workThread = new WorkThread();
		workThread.start();
		message("开始采集指纹，请按压同一手指指纹三次.",true);
	}

	private static int byteArrayToInt(byte[] bytes) {
		int number = bytes[0] & 0xFF;
		number |= ((bytes[1] << 8) & 0xFF00);
		number |= ((bytes[2] << 16) & 0xFF0000);
		number |= ((bytes[3] << 24) & 0xFF000000);
		return number;
	}

	private void freeSensor() {
		try {
			Platform.runLater(new Runnable() {
				public void run() {
					secondButton.setVisible(true);
					firstButton.setVisible(true);
				}
			});
			
			mbStop = true;
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			if (0 != mhDB) {
				FingerprintSensorEx.DBFree(mhDB);
				mhDB = 0;
			}
			if (0 != mhDevice) {
				FingerprintSensorEx.CloseDevice(mhDevice);
				mhDevice = 0;
			}
			FingerprintSensorEx.Terminate();
		}catch (Exception e) {
		}
	}

	private class WorkThread extends Thread {
        @Override
        public void run() {
            super.run();
            int ret = 0;
            while (!mbStop) {
            	templateLen[0] = 2048;
            	if (0 == (ret = FingerprintSensorEx.AcquireFingerprint(mhDevice, imgbuf, template, templateLen)))
            	{
            		if (nFakeFunOn == 1)
                	{
                		byte[] paramValue = new byte[4];
        				int[] size = new int[1];
        				size[0] = 4;
        				int nFakeStatus = 0;
        				//GetFakeStatus
        				ret = FingerprintSensorEx.GetParameters(mhDevice, 2004, paramValue, size);
        				nFakeStatus = byteArrayToInt(paramValue);
        				System.out.println("ret = "+ ret +",nFakeStatus=" + nFakeStatus);
        				if (0 == ret && (byte)(nFakeStatus & 31) != 31)
        				{
        		            message("非法指纹?", false);
        					return;
        				}
                	}
                	OnCatpureOK(imgbuf);
                	OnExtractOK(template, templateLen[0]);
                	//String strBase64 = FingerprintSensorEx.BlobToBase64(template, templateLen[0]);
                	//System.out.println("strBase64=" + strBase64);
            	}
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }
    }
	
	private void OnCatpureOK(byte[] imgBuf)
	{
	}
	
	
	public static void writeBitmap(byte[] imageBuf, int nWidth, int nHeight,
			String path) throws IOException {
		java.io.FileOutputStream fos = new java.io.FileOutputStream(path);
		java.io.DataOutputStream dos = new java.io.DataOutputStream(fos);

		int w = (((nWidth+3)/4)*4);
		int bfType = 0x424d; 
		int bfSize = 54 + 1024 + w * nHeight;
		int bfReserved1 = 0;
		int bfReserved2 = 0;
		int bfOffBits = 54 + 1024;

		dos.writeShort(bfType); 
		dos.write(changeByte(bfSize), 0, 4); 
		dos.write(changeByte(bfReserved1), 0, 2);
		dos.write(changeByte(bfReserved2), 0, 2);
		dos.write(changeByte(bfOffBits), 0, 4);

		int biSize = 40;
		int biWidth = nWidth;
		int biHeight = nHeight;
		int biPlanes = 1; 
		int biBitcount = 8;
		int biCompression = 0;
		int biSizeImage = w * nHeight;
		int biXPelsPerMeter = 0;
		int biYPelsPerMeter = 0;
		int biClrUsed = 0;
		int biClrImportant = 0;

		dos.write(changeByte(biSize), 0, 4);
		dos.write(changeByte(biWidth), 0, 4);
		dos.write(changeByte(biHeight), 0, 4);
		dos.write(changeByte(biPlanes), 0, 2);
		dos.write(changeByte(biBitcount), 0, 2);
		dos.write(changeByte(biCompression), 0, 4);
		dos.write(changeByte(biSizeImage), 0, 4);
		dos.write(changeByte(biXPelsPerMeter), 0, 4);
		dos.write(changeByte(biYPelsPerMeter), 0, 4);
		dos.write(changeByte(biClrUsed), 0, 4);
		dos.write(changeByte(biClrImportant), 0, 4);

		for (int i = 0; i < 256; i++) {
			dos.writeByte(i);
			dos.writeByte(i);
			dos.writeByte(i);
			dos.writeByte(0);
		}

		byte[] filter = null;
		if (w > nWidth)
		{
			filter = new byte[w-nWidth];
		}
		
		for(int i=0;i<nHeight;i++)
		{
			dos.write(imageBuf, (nHeight-1-i)*nWidth, nWidth);
			if (w > nWidth)
				dos.write(filter, 0, w-nWidth);
		}
		dos.flush();
		dos.close();
		fos.close();
	}
	public static byte[] changeByte(int data) {
		return intToByteArray(data);
	}
	
	public static byte[] intToByteArray (final int number) {
		byte[] abyte = new byte[4];  
	    abyte[0] = (byte) (0xff & number);  
	    abyte[1] = (byte) ((0xff00 & number) >> 8);  
	    abyte[2] = (byte) ((0xff0000 & number) >> 16);  
	    abyte[3] = (byte) ((0xff000000 & number) >> 24);  
	    return abyte; 
	}	 
	
	private byte[][] regtemparray = new byte[3][2048];
	private int iFid = 1;
	private int nFakeFunOn = 1;
	private int enroll_idx = 0;
	private byte[] lastRegTemp = new byte[2048];
	private int cbRegTemp = 0;
	
	private void OnExtractOK(byte[] template, int len){
		int[] fid = new int[1];
		int[] score = new int [1];
        int ret = FingerprintSensorEx.DBIdentify(mhDB, template, fid, score);
        if (ret == 0)
        {
            message("该手指指纹已注册，请更换其它手指指纹.", false);
            enroll_idx = 0;
            return;
        }
        if (enroll_idx > 0 && FingerprintSensorEx.DBMatch(mhDB, regtemparray[enroll_idx-1], template) <= 0)
        {
            message("采集时，需按压同一手指指纹三次.", true);
        	return;
        }
        System.arraycopy(template, 0, regtemparray[enroll_idx], 0, 2048);
        enroll_idx++;
        if (enroll_idx == 3) {
        	int[] _retLen = new int[1];
            _retLen[0] = 2048;
            byte[] regTemp = new byte[_retLen[0]];
            
            if (0 == (ret = FingerprintSensorEx.DBMerge(mhDB, regtemparray[0], regtemparray[1], regtemparray[2], regTemp, _retLen)) &&
            		0 == (ret = FingerprintSensorEx.DBAdd(mhDB, iFid, regTemp))) {
            	iFid++;
            	cbRegTemp = _retLen[0];
                System.arraycopy(regTemp, 0, lastRegTemp, 0, cbRegTemp);
                String strBase64 = FingerprintSensorEx.BlobToBase64(regTemp, cbRegTemp);
                message("采集成功.", true);
            	User user1=user.get();
            	if(curPos==1) {
                	user1.setFpr1(strBase64);
                	if(user1.update()) {
                		message("采集成功.", true);
             			Platform.runLater(new Runnable() {
            				public void run() {
            					firstEnrollLabel.setText("成功");
            					firstEnrollLabel.setId("success");
            					
            				}
            			});
                	}else {
                		 message("采集失败.", false);
                		 Platform.runLater(new Runnable() {
             				public void run() {
             					firstEnrollLabel.setText("失败");
             					firstEnrollLabel.setId("error");
             				}
             			});
                	}
				}
                else if(curPos==2) {
					user1.setFpr2(strBase64);
                	if(user1.update()) {
                		 message("采集成功.", true);
                		 Platform.runLater(new Runnable() {
             				public void run() {
             					secondEnrollLabel.setText("成功");
             					secondEnrollLabel.setId("success");
             				}
             			});
                	}else {
                		 message("采集失败.", false);
                		 Platform.runLater(new Runnable() {
              				public void run() {
              					secondEnrollLabel.setText("失败");
              					secondEnrollLabel.setId("error");
              				}
              			});
                	}
				}else {
					 message("无效操作.", false);
				}
     			try {
     				FiltUtil.writeFile(System.getProperty("user.dir")+"/fprs/"+user1.getId(),"");
     			}catch (Exception e) {
				}
                freeSensor();
            } else {
                message("采集失败，错误码【"+ret+"】.", false);
                freeSensor();
            }
        } else {
        	message("需按压手指指纹 " + (3 - enroll_idx) + " 次.", true);
        }
	}
	
	private void message(final String text, final boolean isOk) {
		Platform.runLater(new Runnable() {
			public void run() {
				if (isOk) {
					messageLabel.setId("message-label");
				} else {
					messageLabel.setId("message-label-error");
				}
				messageLabel.setText(text);
			}
		});
	}

}
